# 导入必要的库
import requests
from lxml import etree
import csv
import numpy as np
import pandas as pd
from collections import defaultdict
from datetime import datetime
import matplotlib.pyplot as plt
from matplotlib import font_manager
from matplotlib import rcParams
from mpl_toolkits.mplot3d import Axes3D
import imageio.v2 as imageio
from IPython.display import Image, display
from wordcloud import WordCloud
from PIL import Image
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LinearRegression
from sklearn import metrics

# 设置字体支持中文
rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False  # 解决保存图像时负号'-'显示为方块的问题


# 1. 数据爬取
def getWeather(url):
    headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36'
    }
    resp = requests.get(url, headers=headers)
    tree = etree.HTML(resp.text)
    resp_list = tree.xpath("//ul[@class='thrui']/li")

    day_weather_info = []
    for li in resp_list:
        dates = li.xpath('.//div[@class="th200"]/text()')[0]  # 提取日期
        max_temperatures = li.xpath('.//div[@class="th140"]/text()')[0]  # 提取最高气温
        min_temperatures = li.xpath('.//div[@class="th140"][2]/text()')[0]  # 提取最低气温
        weather_conditions = li.xpath('.//div[@class="th140"][3]/text()')[0]  # 提取天气情况
        wind_directions = li.xpath('.//div[@class="th140"][4]/text()')[0].split(' ')[0]  # 提取风向
        wind_speeds = li.xpath('.//div[@class="th140"][4]/text()')[0].split(' ')[1]  # 提取风速

        day_weather_info.append({
            "日期": dates,
            "最高气温": max_temperatures,
            "最低气温": min_temperatures,
            "天气情况": weather_conditions,
            "风向": wind_directions,
            "风速": wind_speeds
        })
    return day_weather_info


# 爬取东莞市2023年1-12月的天气数据
weathers = []
for month in range(1, 13):
    url = f'https://lishi.tianqi.com/dongguan/2023{str(month).zfill(2)}.html'
    weather_data = getWeather(url)
    weathers.extend(weather_data)

# 2. 数据存储
with open("weather_data.csv", "w", newline='', encoding='utf-8') as csvfile:
    writer = csv.writer(csvfile)
    writer.writerow(["日期", "最高气温", "最低气温", "天气情况", "风向", "风速"])
    for day_weather in weathers:
        writer.writerow([
            day_weather["日期"],
            day_weather["最高气温"],
            day_weather["最低气温"],
            day_weather["天气情况"],
            day_weather["风向"],
            day_weather["风速"]
        ])
print("数据已成功存储到 weather_data.csv")

# 3. 数据读取与处理
with open("weather_data.csv", "r", newline='', encoding='utf-8') as csvfile:
    reader = csv.reader(csvfile)
    data = list(reader)
# 数据标准化处理
formatted_data = []
for item in data[1:]:  # 跳过表头
    formatted_item = {
        '日期时间': item[0],
        '最高温度': float(item[1].replace('°', '')),
        '最低温度': float(item[2].replace('°', '')),
        '天气状况': item[3]
    }
    formatted_data.append(formatted_item)

# 按月份分组
monthly_data = defaultdict(list)
for item in formatted_data:
    month = datetime.strptime(item['日期时间'], '%Y-%m-%d').month
    monthly_data[month].append(item)


# 4. 数据可视化
def show(date_time, high_temp, low_temp, weather, month):
    # 折线图
    plt.plot(date_time, high_temp, label='最高温度')
    plt.plot(date_time, low_temp, label='最低温度')
    plt.xlabel('日期时间')
    plt.ylabel('温度')
    plt.title(f'{month}月最高温度和最低温度折线图')
    plt.legend()
    plt.savefig(f'plot_{month}.jpg')
    plt.show()

    # 散点图
    plt.scatter(date_time, high_temp, label='最高温度')
    plt.scatter(date_time, low_temp, label='最低温度')
    plt.xlabel('日期时间')
    plt.ylabel('温度')
    plt.title(f'{month}月最高温度和最低温度散点图')
    plt.legend()
    plt.savefig(f'scatter_{month}.jpg')
    plt.show()

    # 饼状图
    weather_counts = {w: weather.count(w) for w in set(weather)}
    plt.pie(weather_counts.values(), labels=weather_counts.keys(), autopct='%1.1f%%')
    plt.title(f'{month}月天气状况分布饼状图')
    plt.savefig(f'pie_{month}.jpg')
    plt.show()

    # 热力图
    temp_range = np.array([h - l for h, l in zip(high_temp, low_temp)])
    plt.imshow(temp_range.reshape(-1, 1), cmap='hot', aspect='auto')
    plt.colorbar(label='温度差')
    plt.title(f'{month}月最高温度和最低温度热力图')
    plt.savefig(f'hot_{month}.jpg')
    plt.show()

    # 3D曲面图
    fig = plt.figure(figsize=(8, 5), dpi=100)
    ax = fig.add_subplot(111, projection='3d')
    ax.plot_trisurf(high_temp, low_temp, np.arange(len(high_temp)), cmap='viridis', edgecolor='none')
    ax.set_xlabel('最高温度')
    ax.set_ylabel('最低温度')
    ax.set_title(f'{month}月最高温度和最低温度3D曲面图')
    plt.savefig(f'3D_{month}.jpg')
    plt.show()


# 为每个月生成可视化图表
for month in range(1, 13):
    if month in monthly_data:
        date_time = [item['日期时间'][-2:] for item in monthly_data[month]]
        high_temp = [item['最高温度'] for item in monthly_data[month]]
        low_temp = [item['最低温度'] for item in monthly_data[month]]
        weather = [item['天气状况'] for item in monthly_data[month]]
        show(date_time, high_temp, low_temp, weather, month)


# 5. 词云图
def generate_wordcloud(image_path, font_path, weather_data, save_path=None):
    mask_array = np.array(Image.open(image_path))
    weather_str = ''.join(weather_data)
    wordcloud = WordCloud(
        background_color='white',
        font_path=font_path,
        mask=mask_array,
        max_words=len(weather_str)
    ).generate(weather_str)
    plt.imshow(wordcloud, interpolation='bilinear')
    plt.axis('off')
    if save_path:
        plt.savefig(save_path)
        print(f"词云图已保存至 {save_path}")
    else:
        plt.show()


# 示例：生成静态词云图
image_path = "basketball.jpg"  # 需要替换为你的图片路径
font_path = 'simhei.ttf'  # 需要替换为你的字体路径
weather_data = [item['天气状况'] for item in formatted_data]
generate_wordcloud(image_path, font_path, weather_data, save_path="weather_wordcloud.jpg")

# 6. 模型预测
# 准备数据
df = pd.DataFrame(formatted_data)
X = df[['最高温度']].astype(float)  # 特征变量
y = df['最低温度'].astype(float)  # 目标变量

# 划分训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.5, random_state=0)

# 训练线性回归模型
model = LinearRegression()
model.fit(X_train, y_train)

# 预测
y_pred = model.predict(X_test)

# 可视化回归结果
plt.scatter(X_test, y_test, color='blue', label='真实值')
plt.plot(X_test, y_pred, color='red', label='预测值')
plt.title('最高气温与最低气温的线性回归模型')
plt.xlabel('最高气温')
plt.ylabel('最低气温')
plt.legend()
plt.show()

# 评估模型
print(f"模型的 R² 分数: {model.score(X_test, y_test)}")